home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-19 / iritsm3s.zip / INPTPRSR.C < prev    next >
C/C++ Source or Header  |  1992-02-23  |  33KB  |  979 lines

  1. /*****************************************************************************
  2. *   "Irit" - the 3d polygonal solid modeller.                     *
  3. *                                         *
  4. * Written by:  Gershon Elber                Ver 0.2, Mar. 1990   *
  5. ******************************************************************************
  6. *   Module to convert infix expression given as    ascii stream sequence into   *
  7. * a binary tree, and evaluate it.                         *
  8. *   All the objects are handled the same but the numerical one, which is     *
  9. * moved as a RealType and not as an object (only internally within this         *
  10. * module) as it is frequently used and consumes much less memory this way.   *
  11. *****************************************************************************/
  12.  
  13. #ifdef __MSDOS__
  14. #include <alloc.h>
  15. #endif /* __MSDOS__ */
  16.  
  17. #include <stdio.h>
  18. #include <ctype.h>
  19. #include <math.h>
  20. #include <string.h>
  21. #include "program.h"
  22. #include "allocate.h"
  23. #include "ctrl-brk.h"
  24. #include "graphgen.h"
  25. #include "inptprsg.h"
  26. #include "inptprsl.h"
  27. #include "objects.h"
  28. #include "overload.h"
  29. #include "windows.h"
  30.  
  31. static int IPGlblILastToken;              /* Globals used by parser. */
  32. static InptPrsrEvalErrType IPGlblParseError = IPE_NO_ERR;
  33. char IPGlblCharData[LINE_LEN_LONG];          /* Used for both parse & eval. */
  34. static FileStackStruct FileStack[FILE_STACK_SIZE];    /* Include file stack. */
  35. static int FileStackPtr = 0;
  36.  
  37. /* Operator preceeding parser stack, and stack pointer: */
  38. static ParseTree *Stack[MAX_PARSER_STACK];
  39. static int ParserStackPointer = 0;
  40.  
  41. /* Aliases list - simple macro substitution mechanizem. */
  42. static AliasesStruct GlobalAliasList;
  43.  
  44. #ifdef DEBUG
  45.     int MaxStackPointer = 0;          /* Measure maximum depth of stack. */
  46. #endif /* DEBUG */
  47.  
  48. static ParseTree *GenInputParseTree(void);
  49. static ParseTree *OperatorPrecedence(void);
  50. static int TestPreceeding(int Token1, int Token2);
  51. static int GetToken(RealType *Data);
  52. static int GetVarFuncToken(char *Token, RealType *Data);
  53. static void FlushToEndOfExpr(int FlushStdin);
  54. static int GetEchoSrc(void);
  55. static void InptPrsrUnGetC(char c);
  56. static char InptPrsrGetC(void);
  57.  
  58. /*****************************************************************************
  59. *   Main module routine - generate parse tree and then tries to evaluate it. *
  60. * Returns TRUE if succesfull, otherwise check IPGlblParseError/EvalError.    *
  61. *****************************************************************************/
  62. int InputParser(void)
  63. {
  64.     ParseTree *PTree;
  65.  
  66.     if (GlblWasCtrlBrk || GlblFatalError) {
  67.     GlblFatalError = GlblWasCtrlBrk = FALSE;
  68.     FlushToEndOfExpr(FALSE);      /* Close all include files if any. */
  69.     return TRUE;
  70.     }
  71.  
  72.     PTree = GenInputParseTree();             /* Generate parse tree. */
  73.  
  74.     if (IPGlblParseError == IPE_NO_ERR) {
  75. #    ifdef DEBUG
  76.         fprintf(stderr, "\nInput generated Parse tree (Max stack = %d)\n",
  77.                             MaxStackPointer);
  78.         InptPrsrPrintTree(PTree, NULL);
  79.         fprintf(stderr, "\n");
  80. #    endif /* DEBUG */
  81.     if (InptPrsrTypeCheck(PTree, 0) == ERROR_EXPR) {   /* Type checking. */
  82.         InptPrsrFreeTree(PTree);             /* Not needed any more. */
  83.         FlushToEndOfExpr(TRUE);/* Close all include files, & flush stdin.*/
  84.         return FALSE;
  85.     }
  86.  
  87.     InptPrsrEvalTree(PTree, 0);                 /* Evaluate it. */
  88.     if (IPGlblEvalError != IPE_NO_ERR) {
  89.         FlushToEndOfExpr(TRUE); /* Close include files, and flush stdin. */
  90.         return FALSE;
  91.     }
  92.     }
  93.     else {
  94.     FlushToEndOfExpr(TRUE); /* Close all include files, and flush stdin. */
  95.     return FALSE;
  96.     }
  97.  
  98.     InptPrsrFreeTree(PTree);                 /* Not needed any more. */
  99.  
  100.     return !(IPGlblParseError || IPGlblEvalError);
  101. }
  102.  
  103. /*****************************************************************************
  104. *   Routine to convert the expression from stream f into a binary tree.      *
  105. * Algorithm: Using operator precedence with the following grammer:           *
  106. * EXPR    ::= EXPR    |  EXPR + EXPR    |  EXPR - EXPR                       *
  107. * EXPR    ::= EXPR    |  EXPR * EXPR    |  EXPR / EXPR                       *
  108. * EXPR    ::= EXPR    |  EXPR ^ EXPR                                         *
  109. * EXPR    ::= EXPR    |  EXPR , EXPR    |  EXPR = EXPR                       *
  110. * EXPR    ::= NUMBER  |  -EXPR          |  (EXPR)        |  FUNCTION         *
  111. * FUCTION ::= FUNC(EXPR , EXPR , ...)                         *
  112. * Where FUNC might be function like arithmetics (SIN, COS etc.).         *
  113. * Note that FUNC might have more than one operand, seperated by ','.         *
  114. *                                                                            *
  115. * Note the stream is terminated by semicolon character ';'.             *
  116. *                                                                            *
  117. * Left associativity for +, -, *, /, ^.                                      *
  118. * Precedence of operators is as usual:                                       *
  119. *     <Highest> {unar minus}   {^}   {*, /}   {+, -} <Lowest>             *
  120. *                                                                            *
  121. * Returns NULL if an error was found, and error is in IPGlblParseError       *
  122. *****************************************************************************/
  123. static ParseTree *GenInputParseTree(void)
  124. {
  125.     ParseTree *Root;
  126.     int i;
  127.  
  128.     IPGlblILastToken = 0;    /* Used to hold last token read from stream. */
  129.     IPGlblParseError = IPE_NO_ERR;             /* No errors so far ... */
  130.  
  131.     Root = OperatorPrecedence();
  132.  
  133.     if (IPGlblParseError) {
  134.     /* Free partialy allocated tree. */
  135.     for (i = 0; i <= ParserStackPointer; i++) InptPrsrFreeTree(Stack[i]);
  136.         return NULL;                          /* Error ! */
  137.     }
  138.     else
  139.     return Root;
  140. }
  141.  
  142. /*****************************************************************************
  143. *  Routine to allocate new ParseTree expression node:                 *
  144. *****************************************************************************/
  145. ParseTree *MyExprMalloc(void)
  146. {
  147.     ParseTree *p;
  148.  
  149.     p = (ParseTree *) MyMalloc(sizeof(ParseTree), ALLOC_OTHER);
  150.     p -> Right = p -> Left = NULL;
  151.     p -> NodeKind = p -> ObjType = UNDEF_OBJ;
  152.     return p;
  153. }
  154.  
  155. /*****************************************************************************
  156. *  Routine to free one expression node:                         *
  157. *****************************************************************************/
  158. void MyExprFree(ParseTree *Ptr)
  159. {
  160.     MyFree((char *) Ptr, ALLOC_OTHER);
  161. }
  162.  
  163. /*****************************************************************************
  164. *   Routine to actually parse using operator precedence:                     *
  165. * Few Notes:                                                                 *
  166. * 1. Parse the input with the help of GetToken routine. Input is redirected  *
  167. *    using the FileStack.                             *
  168. * 2. All tokens must be in the range of 0..999 as we use the numbers above   *
  169. *    it (adding 1000) to deactivate them in the handle searching (i.e. when  *
  170. *    they were reduced to sub.-expression).                                  *
  171. * 3. Returns NULL pointer in case of an error (see Expr2TrG.h for errors     *
  172. * 4. See "Compilers - principles, techniques and tools" by Aho, Sethi &      *
  173. *    Ullman,   pages 207-210.                                                *
  174. *****************************************************************************/
  175. static ParseTree *OperatorPrecedence(void)
  176. {
  177.     int Token, LowHandle, Temp1, Temp2;
  178.     RealType Data;
  179.  
  180. #   ifdef DEBUG
  181.     MaxStackPointer = 0;
  182. #   endif /* DEBUG */
  183.  
  184.     ParserStackPointer = 0;
  185.  
  186.     /* Push the start symbol on stack (node pointer points on tos): */
  187.     Stack[ParserStackPointer] = MyExprMalloc();
  188.     Stack[ParserStackPointer] -> NodeKind = TOKENSTART;
  189.     Stack[ParserStackPointer] -> Right =
  190.     Stack[ParserStackPointer] -> Left = NULL;
  191.  
  192.     Token = GetToken(&Data);      /* Get one look ahead token to start with. */
  193.  
  194.     do {
  195.         if (IPGlblParseError) return NULL;
  196.  
  197.         Temp1 = ParserStackPointer;       /* Find top active token (<1000). */
  198.         while (Stack[Temp1] -> NodeKind >= 1000) Temp1--;
  199.         /* Now test to see if the new token completes an handle: */
  200.         if (TestPreceeding(Stack[Temp1] -> NodeKind, Token)) {
  201.             switch (Token) {
  202.         case CLOSPARA:
  203.                     if (Stack[Temp1] -> NodeKind == OPENPARA) {
  204.             MyExprFree(Stack[Temp1]);     /* Free open paran. */
  205.             /* If a parameter is introduced instead of function  */
  206.             /* it will be reduced already against "(" and it     */
  207.             /* probably was missspelled function...             */
  208.                         if (Stack[Temp1-1] -> NodeKind == PARAMETER+1000) {
  209.                 strcpy(IPGlblCharData,
  210.                     Stack[Temp1-1] -> U.PObj -> Name);
  211.                 IPGlblParseError = IP_ERR_UNDEF_FUNC;
  212.                 return NULL;
  213.             }
  214.  
  215.                         if (IS_NO_PARAM_FUNC(Stack[Temp1-1] -> NodeKind)) {
  216.                 if (ParserStackPointer-Temp1 == 1) {
  217.                     UpdateCharError("",
  218.                         Stack[Temp1-1] -> NodeKind);
  219.                 IPGlblParseError = IP_ERR_NO_PARAM_FUNC;
  220.                 return NULL;
  221.                 }
  222.                 Stack[Temp1-1] -> NodeKind += 1000;
  223.                 Stack[Temp1-1] -> Right = NULL;
  224.                 ParserStackPointer--;
  225.                         }
  226.                         else if (IS_FUNCTION(Stack[Temp1-1] -> NodeKind)) {
  227.                 if (ParserStackPointer-Temp1 != 1) {
  228.                     UpdateCharError("",
  229.                         Stack[Temp1-1] -> NodeKind);
  230.                     IPGlblParseError = IP_ERR_PARAM_FUNC;
  231.                     return NULL;
  232.                 }
  233.                 Stack[ParserStackPointer] -> NodeKind -= 1000;
  234.                 Stack[Temp1-1] -> NodeKind += 1000;
  235.                 Stack[Temp1-1] -> Right =
  236.                     Stack[ParserStackPointer];
  237.                 ParserStackPointer -= 2;
  238.                     }
  239.                         else {
  240.                 if (ParserStackPointer-Temp1 != 1) {
  241.                     IPGlblParseError = IP_ERR_PARAM_MATCH;
  242.                     return NULL;
  243.                 }
  244.                             Stack[Temp1] = Stack[ParserStackPointer--];
  245.             }
  246.                         Token = GetToken(&Data);       /* Get another token. */
  247.                         continue;
  248.             }
  249.             else if (Stack[Temp1] -> NodeKind == TOKENSTART) {
  250.             /* No match for this one! */
  251.                         IPGlblParseError = IP_ERR_PARAM_MATCH;
  252.             return NULL;
  253.             }
  254.             break;
  255.                 case TOKENEND:
  256.                     if (Stack[Temp1] -> NodeKind == TOKENSTART) {
  257.                         if (ParserStackPointer != 1) {
  258.                             IPGlblParseError = IP_ERR_WRONG_SYNTAX;
  259.                 return NULL;
  260.             }
  261.             InptPrsrFreeTree(Stack[Temp1]);      /* The TOKENSTART. */
  262.             Stack[1] -> NodeKind -= 1000;
  263.             return Stack[1];
  264.             }
  265.         }
  266.  
  267.             Temp2 = Temp1 - 1;          /* Find the lower bound of handle. */
  268.             while (Temp2 >= 0 && Stack[Temp2] -> NodeKind >= 1000) Temp2--;
  269.             LowHandle = Temp2 + 1;
  270.             if (LowHandle < 1) {                  /* No low bound was found. */
  271.                 IPGlblParseError = IP_ERR_WRONG_SYNTAX;
  272.             return NULL;             /* We ignore data till now. */
  273.             }
  274.         switch (ParserStackPointer - LowHandle + 1) {
  275.         case 1: /* Its a scalar one - mark it as used (add 1000). */
  276.             switch (Stack[ParserStackPointer] -> NodeKind) {
  277.             case NUMBER:
  278.             case PARAMETER:
  279.             case STRING:
  280.                     Stack[ParserStackPointer] -> NodeKind += 1000;
  281.                 break;
  282.             default:
  283.                 UpdateCharError("Found ",
  284.                 Stack[ParserStackPointer] -> NodeKind);
  285.                 IPGlblParseError = IP_ERR_PARAM_EXPECT;
  286.                 return NULL;
  287.             }
  288.             break;
  289.         case 2: /* Its a monadic operator - create the subtree. */
  290.             switch (Stack[ParserStackPointer-1] -> NodeKind) {
  291.                 case UNARMINUS:
  292.                     Stack[ParserStackPointer-1] -> Right =
  293.                         Stack[ParserStackPointer];
  294.                     Stack[ParserStackPointer] -> NodeKind -= 1000;
  295.                     Stack[ParserStackPointer-1] -> NodeKind += 1000;
  296.                     ParserStackPointer--;
  297.                     break;
  298.                 case OPENPARA:
  299.                 IPGlblParseError = IP_ERR_PARAM_MATCH;
  300.                 return NULL;
  301.                 default:
  302.                 UpdateCharError("Found Operator ",
  303.                 Stack[ParserStackPointer-1] -> NodeKind);
  304.                 IPGlblParseError = IP_ERR_ONE_OPERAND;
  305.                 return NULL;
  306.             }
  307.             break;
  308.         case 3: /* Its a diadic operator - create the subtree. */
  309.             switch (Stack[ParserStackPointer-1] -> NodeKind) {
  310.                 case PLUS:
  311.                 case MINUS:
  312.                 case MULT:
  313.                 case DIV:
  314.                 case POWER:
  315.                 case COMMA:
  316.                 case EQUAL:
  317.                 case COLON:
  318.                     Stack[ParserStackPointer-1] -> Right =
  319.                                   Stack[ParserStackPointer];
  320.                             Stack[ParserStackPointer-1] -> Left =
  321.                                   Stack[ParserStackPointer-2];
  322.                     Stack[ParserStackPointer-2] -> NodeKind -= 1000;
  323.                     Stack[ParserStackPointer] -> NodeKind -= 1000;
  324.                     Stack[ParserStackPointer-1] -> NodeKind += 1000;
  325.                     Stack[ParserStackPointer-2] =
  326.                         Stack[ParserStackPointer-1];
  327.                     ParserStackPointer -= 2;
  328.                             break;
  329.                         default:
  330.                 UpdateCharError("Found Operator ",
  331.                 Stack[ParserStackPointer-1] -> NodeKind);
  332.                 IPGlblParseError = IP_ERR_TWO_OPERAND;
  333.                 return NULL;
  334.             }
  335.             break;
  336.         default:
  337.             IPGlblParseError = IP_ERR_WRONG_SYNTAX;
  338.             return NULL;
  339.         }
  340.         }
  341.         else {         /* Push that token on stack - it is not handle yet. */
  342.         Stack[++ParserStackPointer] = MyExprMalloc();
  343.  
  344. #        ifdef DEBUG
  345.         if (MaxStackPointer < ParserStackPointer)
  346.             MaxStackPointer = ParserStackPointer;
  347. #        endif /* DEBUG */
  348.  
  349.             if (ParserStackPointer == MAX_PARSER_STACK-1) {
  350.                 IPGlblParseError = IP_ERR_STACK_OV;
  351.         return NULL;             /* We ignore data till now. */
  352.         }
  353.             Stack[ParserStackPointer] -> NodeKind = Token;
  354.             Stack[ParserStackPointer] -> U.R = Data;  /* We might need that. */
  355.         Stack[ParserStackPointer] -> Right =
  356.         Stack[ParserStackPointer] -> Left = NULL;
  357.         if (Token == PARAMETER) {
  358.         if ((Stack[ParserStackPointer] -> U.PObj =
  359.                 GetObject(IPGlblCharData)) == NULL) {
  360.             /* Its new one - allocate memory for it. */
  361.             Stack[ParserStackPointer] -> U.PObj =
  362.             AllocObject(IPGlblCharData, UNDEF_OBJ, NULL);
  363.         }
  364.         Stack[ParserStackPointer] -> U.PObj->Count++;
  365.         }
  366.         else if (Token == STRING) {
  367.         Stack[ParserStackPointer] -> U.PObj =
  368.             AllocObject("", STRING_OBJ, NULL);
  369.         strcpy(Stack[ParserStackPointer] -> U.PObj -> U.Str,
  370.                IPGlblCharData);
  371.         Stack[ParserStackPointer] -> U.PObj->Count++;
  372.         }
  373.             Token = GetToken(&Data);       /* And get new token from stream. */
  374.     }
  375.     }
  376.     while (TRUE);
  377. }
  378.  
  379. /*****************************************************************************
  380. *   Routine to test precedence of two tokens. returns 0, <0 or >0 according  *
  381. * to comparison results:                                                     *
  382. *****************************************************************************/
  383. static int TestPreceeding(int Token1, int Token2)
  384. {
  385.     int Preced1, Preced2;
  386.  
  387.     if ((Token1 >= 1000) || (Token2 >= 1000))
  388.     return FALSE;                     /* Ignore sub-expr. */
  389.  
  390.     if (IS_FUNCTION(Token1))
  391.     Preced1 = 130;
  392.     else {
  393.         switch (Token1) {
  394.             case COMMA:
  395.             case COLON:
  396.             case EQUAL:
  397.             Preced1 = 30;
  398.             break;
  399.             case NUMBER:
  400.             case PARAMETER:
  401.             case STRING:
  402.             Preced1 = 150;
  403.             break;
  404.         case PLUS:
  405.         case MINUS:
  406.             Preced1 = 50;
  407.             break;
  408.         case MULT:
  409.         case DIV:
  410.             Preced1 = 70;
  411.             break;
  412.         case POWER:
  413.             Preced1 = 90;
  414.             break;
  415.         case UNARMINUS:
  416.             Preced1 = 95;
  417.             break;
  418.         case OPENPARA:
  419.             Preced1 = 20;
  420.             break;
  421.         case CLOSPARA:
  422.             Preced1 = 150;
  423.             break;
  424.         case TOKENSTART:
  425.         case TOKENEND:
  426.             Preced1 = 10;
  427.             break;
  428.         }
  429.     }
  430.  
  431.     if (IS_FUNCTION(Token2))
  432.     Preced2 = 120;
  433.     else {
  434.         switch (Token2) {
  435.         case COMMA:
  436.         case COLON:
  437.         case EQUAL:
  438.             Preced2 = 35;
  439.             break;
  440.         case NUMBER:
  441.         case PARAMETER:
  442.         case STRING:
  443.             Preced2 = 140;
  444.             break;
  445.         case PLUS:
  446.         case MINUS:
  447.             Preced2 = 40;
  448.             break;
  449.         case MULT:
  450.         case DIV:
  451.             Preced2 = 60;
  452.             break;
  453.         case POWER:
  454.             Preced2 = 80;
  455.             break;
  456.         case UNARMINUS:
  457.             Preced2 = 100;
  458.             break;
  459.         case OPENPARA:
  460.             Preced2 = 140;
  461.             break;
  462.         case CLOSPARA:
  463.             Preced2 = 15;
  464.             break;
  465.         case TOKENSTART:
  466.         case TOKENEND:
  467.             Preced2 = 0;
  468.             break;
  469.         }
  470.     }
  471.  
  472.     return Preced1-Preced2 > 0;
  473. }
  474.  
  475. /*****************************************************************************
  476. *  Routine to update the character error message according to StrMsg & Token *
  477. *****************************************************************************/
  478. void UpdateCharError(char *StrMsg, int Token)
  479. {
  480.     char *TokenChar = NULL;
  481.  
  482.     if (Token > 1000) Token -= 1000;
  483.  
  484.     if (IS_NUM_FUNCTION(Token))
  485.     TokenChar = NumFuncTable[Token-NUM_FUNC_OFFSET].FuncName;
  486.     else if (IS_OBJ_FUNCTION(Token))
  487.     TokenChar = ObjFuncTable[Token-OBJ_FUNC_OFFSET].FuncName;
  488.     else if (IS_GEN_FUNCTION(Token))
  489.     TokenChar = GenFuncTable[Token-GEN_FUNC_OFFSET].FuncName;
  490.     else {
  491.         switch (Token) {
  492.             case PLUS:
  493.             TokenChar = "+";
  494.             break;
  495.             case MINUS:
  496.             TokenChar = "-";
  497.             break;
  498.         case MULT:
  499.             TokenChar = "*";
  500.             break;
  501.         case DIV:
  502.             TokenChar = "/";
  503.             break;
  504.         case POWER:
  505.             TokenChar = "^";
  506.             break;
  507.         case UNARMINUS:
  508.             TokenChar = "(Unar) -";
  509.             break;
  510.         case COMMA:
  511.             TokenChar = ",";
  512.             break;
  513.         case EQUAL:
  514.             TokenChar = "=";
  515.             break;
  516.         case COLON:
  517.             TokenChar = ":";
  518.             break;
  519.         default:
  520.             sprintf(IPGlblCharData, "%s - Token %d\n", StrMsg, Token);
  521.             return;
  522.         }
  523.     }
  524.     sprintf(IPGlblCharData, "%s%s", StrMsg, TokenChar);
  525. }
  526.  
  527. /*****************************************************************************
  528. *   Routine to get the next token out of the expression.                     *
  529. * Gets the expression in S, and current position in i.                       *
  530. * Returns the next token found, set data to the returned value (if any),     *
  531. * and update i to one char ofter the new token found.                        *
  532. *   Note that in minus sign case, it is determined whether it is monadic or  *
  533. * diadic minus by the last token - if the last token was operator or '('     *
  534. * it is monadic minus.                                                       *
  535. *****************************************************************************/
  536. static int GetToken(RealType *Data)
  537. {
  538.     int i, RetVal = 0;
  539.     char c;
  540.  
  541.     while (isspace(c = InptPrsrGetC()));           /* Skip white blanks. */
  542.  
  543.     if (c == '"') {          /* Its a string token - read up to next ". */
  544.     i = 0;
  545.     while ((IPGlblCharData[i] = InptPrsrGetC()) != '"') {
  546.         if (IPGlblCharData[i] == '\\') /* Its escape char. for next one: */
  547.         IPGlblCharData[i] = InptPrsrGetC();
  548.         i++;
  549.     }
  550.     IPGlblCharData[i] = 0;
  551.     RetVal = STRING;
  552.     }
  553.     else if (isalpha(c)) {          /* Is it a variable/function name? */
  554.     if (islower(c))
  555.         IPGlblCharData[i=0] = toupper(c);
  556.     else
  557.         IPGlblCharData[i=0] = c;
  558.  
  559.     while (isalpha(c = InptPrsrGetC()) || isdigit(c) || c == '_')
  560.         if (islower(c))
  561.         IPGlblCharData[++i] = toupper(c);
  562.         else
  563.         IPGlblCharData[++i] = c;
  564.     IPGlblCharData[++i] = 0;
  565.     InptPrsrUnGetC(c);
  566.  
  567.     if ((int) strlen(IPGlblCharData) >= OBJ_NAME_LEN) {
  568.         RetVal = TOKENERROR;
  569.         IPGlblParseError = IP_ERR_NAME_TOO_LONG;
  570.     }
  571.     else {
  572.         RetVal = GetVarFuncToken(IPGlblCharData, Data);
  573.     }
  574.     }
  575.     else if (isdigit(c) || (c == '.')) {          /* Is it numeric data? */
  576.     IPGlblCharData[i=0] = c;
  577.  
  578.     while (isdigit(c = InptPrsrGetC()) || (c == '.') ||
  579.                     (c == 'e') || (c == 'E') || (c == 'e'))
  580.         IPGlblCharData[++i] = c;
  581.     /* Handle the special case of negative exponent ("111.111E-22"). */
  582.     if (c == '-' && (IPGlblCharData[i] == 'e' ||
  583.              IPGlblCharData[i] == 'E')) {
  584.         IPGlblCharData[++i] = c;
  585.         while (isdigit(c = InptPrsrGetC()) || (c == '.'))
  586.         IPGlblCharData[++i] = c;
  587.     }
  588.     IPGlblCharData[++i] = 0;
  589.  
  590.     InptPrsrUnGetC(c);
  591.  
  592. #    ifdef DOUBLE
  593.         sscanf(IPGlblCharData, "%lf", Data);
  594. #    else
  595.         sscanf(IPGlblCharData, "%f", Data);
  596. #    endif /* DOUBLE */
  597.  
  598.         RetVal = NUMBER;
  599.     }
  600.     else
  601.     switch (c) {
  602.         case '+':
  603.         RetVal = PLUS;
  604.         break;
  605.         case '-':
  606.         switch (IPGlblILastToken) {
  607.             case 0:          /* If first token (no last token yet). */
  608.             case PLUS:
  609.             case MINUS:
  610.             case MULT:
  611.             case DIV:
  612.             case POWER:
  613.             case COMMA:
  614.             case EQUAL:
  615.             case COLON:
  616.             case UNARMINUS:
  617.             case OPENPARA:
  618.             RetVal = UNARMINUS;
  619.             break;
  620.             default:
  621.                         RetVal = MINUS;
  622.                 break;
  623.         }
  624.         break;
  625.         case '*':
  626.         RetVal = MULT; break;
  627.         case '/':
  628.         RetVal = DIV;
  629.         break;
  630.         case '^':
  631.         RetVal = POWER;
  632.         break;
  633.         case '(':
  634.         RetVal = OPENPARA;
  635.         break;
  636.         case ')':
  637.         RetVal = CLOSPARA;
  638.         break;
  639.         case '=':
  640.         RetVal = EQUAL;
  641.         break;
  642.         case ',':
  643.         RetVal = COMMA;
  644.         break;
  645.         case ':':
  646.         RetVal = COLON;
  647.         break;
  648.         case ';':
  649.         RetVal = TOKENEND;
  650.         break;           /* End of expression! */
  651.         default:
  652.         RetVal = TOKENERROR;
  653.         IPGlblCharData[0] = c;
  654.         IPGlblCharData[1] = 0;
  655.         IPGlblParseError = IP_ERR_UNDEF_TOKEN;
  656.         break;
  657.     }
  658.  
  659.     IPGlblILastToken = RetVal;
  660.  
  661.     return RetVal;
  662. }
  663.  
  664. /*****************************************************************************
  665. *   Routine to test alpha Token for match with one of the defined functions  *
  666. * and returns that Token function if found one. otherwise it is assumed to   *
  667. * be a variable (new or old).                             *
  668. * Note that although the search is linear, It is extremely fast to add new   *
  669. * functions - simply add its token, its entry here, and in the parser itself.*
  670. *****************************************************************************/
  671. static int GetVarFuncToken(char *Token, RealType *Data)
  672. {
  673.     int i;
  674.     char c;
  675.  
  676.     if (strcmp("COMMENT", Token) == 0) {
  677.     /* Get first nonspace char after the COMMENT key word: */
  678.     while (isspace(c = InptPrsrGetC()));
  679.     /* And read the input until this char appear again (end of comment): */
  680.     while (c != InptPrsrGetC());
  681.  
  682.     return GetToken(Data);               /* Return next token instead. */
  683.     }
  684.  
  685.     for (i = 0; i < NumFuncTableSize; i++)        /* Is it Numeric function? */
  686.     if (strcmp(NumFuncTable[i].FuncName, Token) == 0)
  687.         return(NumFuncTable[i].FuncToken);
  688.     for (i = 0; i < ObjFuncTableSize; i++)       /* Is it Object function? */
  689.     if (strcmp(ObjFuncTable[i].FuncName, Token) == 0)
  690.         return(ObjFuncTable[i].FuncToken);
  691.     for (i = 0; i < GenFuncTableSize; i++)      /* Is it General function? */
  692.     if (strcmp(GenFuncTable[i].FuncName, Token) == 0)
  693.         return(GenFuncTable[i].FuncToken);
  694.  
  695.     for (i = 0; i < ConstantTableSize; i++)/* Replace constant by its value. */
  696.     if (strcmp(ConstantTable[i].FuncName, Token) == 0) {
  697.         sprintf(Token, "%lg", ConstantTable[i].Value);
  698.         *Data = ConstantTable[i].Value;
  699.         return NUMBER;
  700.     }
  701.  
  702.     return PARAMETER;   /* If not a function - it is assumed to be variable. */
  703. }
  704.  
  705. /*****************************************************************************
  706. *   Routine to reset the aliases buffer to a known state.             *
  707. *****************************************************************************/
  708. void AliasReset(void)
  709. {
  710.     int i;
  711.  
  712.     for (i = 0; i < NUM_OF_ALIASES; i++)
  713.     GlobalAliasList.Aliases[i].Name = NULL;
  714. }
  715.  
  716. /*****************************************************************************
  717. *   Routine to update (insert, delete or print) the global alias list         *
  718. *****************************************************************************/
  719. void AliasEdit(char *Name, char *Value)
  720. {
  721.     int i;
  722.     char s[LINE_LEN];
  723.  
  724.     if (strlen(Name) == 0) {            /* Print all defined alias list. */
  725.     WndwInputWindowPutStr("Alias List:");
  726.     for (i = 0; i < NUM_OF_ALIASES; i++)
  727.         if (GlobalAliasList.Aliases[i].Name != NULL) {
  728.         sprintf(s, "Alias \"%s\" - \"%s\"",
  729.             GlobalAliasList.Aliases[i].Name,
  730.             GlobalAliasList.Aliases[i].Value);
  731.         WndwInputWindowPutStr(s);
  732.         }
  733.     return;
  734.     }
  735.  
  736.     if (strlen(Value) == 0) {              /* Its a delete operation. */
  737.     for (i = 0; i < NUM_OF_ALIASES; i++)
  738.         if (GlobalAliasList.Aliases[i].Name != NULL &&
  739.         stricmp(Name, GlobalAliasList.Aliases[i].Name) == 0) break;
  740.     if (i<NUM_OF_ALIASES) {         /* Found alias to delete, so delete it. */
  741.         MyFree(GlobalAliasList.Aliases[i].Name, ALLOC_OTHER);
  742.         MyFree(GlobalAliasList.Aliases[i].Value, ALLOC_OTHER);
  743.         GlobalAliasList.Aliases[i].Name = NULL;
  744.     }
  745.     else
  746.         WndwInputWindowPutStr("Alias not found, ignored");
  747.     }
  748.     else {/* Its an insert operation - test for old one, otherwise make new. */
  749.     for (i = 0; i < NUM_OF_ALIASES; i++)
  750.         if (GlobalAliasList.Aliases[i].Name != NULL &&
  751.         stricmp(Name, GlobalAliasList.Aliases[i].Name) == 0) break;
  752.     if (i<NUM_OF_ALIASES) {         /* Found alias to replace - replace it. */
  753.         MyFree(GlobalAliasList.Aliases[i].Value, ALLOC_OTHER);
  754.         GlobalAliasList.Aliases[i].Value =
  755.         MyMalloc(strlen(Value) + 1, ALLOC_OTHER);
  756.         strcpy(GlobalAliasList.Aliases[i].Value, Value);
  757.     }
  758.     else {               /* Find empty slot and insert as new one. */
  759.         for (i = 0; i < NUM_OF_ALIASES; i++)
  760.         if (GlobalAliasList.Aliases[i].Name == NULL) break;
  761.         if (i<NUM_OF_ALIASES) {           /* Found empty slot - use it. */
  762.         GlobalAliasList.Aliases[i].Name =
  763.             MyMalloc(strlen(Name) + 1, ALLOC_OTHER);
  764.         strcpy(GlobalAliasList.Aliases[i].Name, Name);
  765.         GlobalAliasList.Aliases[i].Value =
  766.             MyMalloc(strlen(Value) + 1, ALLOC_OTHER);
  767.         strcpy(GlobalAliasList.Aliases[i].Value, Value);
  768.         }
  769.         else
  770.         WndwInputWindowPutStr("Aliases buffer full, ignored");
  771.     }
  772.     }
  773. }
  774.  
  775. /*****************************************************************************
  776. *   Routine to expand aliases of the given line using the global defined     *
  777. * alias list as saved in GlobalAliasList.                     *
  778. *****************************************************************************/
  779. void AliasExpand(char *Line)
  780. {
  781.     int i, j, OldSize, NewSize, DiffSize, Count = 0;
  782.     char *alias;
  783.  
  784.     for (i = 0; i < NUM_OF_ALIASES; i++)
  785.     if (GlobalAliasList.Aliases[i].Name != NULL) {
  786.         do {
  787.         if ((alias = strstr(Line, GlobalAliasList.Aliases[i].Name)) !=
  788.             NULL) {
  789.             if (Count++ > 100) {
  790.             WndwInputWindowPutStr("Alias expansion - 100 expansions reached created, aborted:");
  791.             return;
  792.             }
  793.             OldSize = strlen(GlobalAliasList.Aliases[i].Name);
  794.             NewSize = strlen(GlobalAliasList.Aliases[i].Value);
  795.             DiffSize = NewSize - OldSize;
  796.             if (DiffSize + (int) strlen(Line) > LINE_LEN_LONG - 1) {
  797.             WndwInputWindowPutStr("Alias expansion created too long line, aborted:");
  798.             return;
  799.             }
  800.             /* Expand/shrink line space according to Name/Value sizes*/
  801.             if (DiffSize != 0)
  802.             if (NewSize > OldSize) {
  803.                 movmem(alias, &alias[DiffSize], strlen(alias) + 1);
  804.             }
  805.             else {
  806.                 movmem(&alias[-DiffSize], alias,
  807.                 strlen(&alias[-DiffSize]) + 1);
  808.             }
  809.             /* And copy the Value instead of name into line: */
  810.             for (j = 0; j < NewSize; j++) alias[j] =
  811.             GlobalAliasList.Aliases[i].Value[j];
  812.         }
  813.         }
  814.         while (alias != NULL);
  815.     }
  816.  
  817. }
  818.  
  819. static char UnGetChar;
  820.  
  821. /*****************************************************************************
  822. *   Routine to get the value to EchoSrc variable.                 *
  823. *****************************************************************************/
  824. static int GetEchoSrc(void)
  825. {
  826.     int EchoSrc;
  827.     ObjectStruct *PObj = GetObject("ECHOSRC");
  828.  
  829.     if (PObj == NULL || !IS_NUM_OBJ(PObj)) {
  830.     WndwInputWindowPutStr("No numeric object name EchoSrc is defined");
  831.     EchoSrc = DEFAULT_ECHOSRC;
  832.     }
  833.     else
  834.     EchoSrc = (int) (PObj -> U.R);
  835.  
  836.     return EchoSrc;
  837. }
  838.  
  839. /*****************************************************************************
  840. *   Routine to control all getchar in this module and echo it if requested   *
  841. * Note it handles the FileStack and decrease it if end of file was found.    *
  842. *****************************************************************************/
  843. static char InptPrsrGetC(void)
  844. {
  845.     static char Line[LINE_LEN_LONG] = "", TLine[LINE_LEN_LONG] = "", *p;
  846.     static int LineLength = 0, LineCount = 0;
  847.     char c;
  848.     int i;
  849.  
  850.     if (UnGetChar == 0) {               /* One level of unget char... */
  851.     if (LineCount < LineLength) {     /* Is there anything in local Line? */
  852.     }
  853.     else
  854.         do {
  855.             if (FileStackPtr == 0) {
  856.             WndwInputWindowGetStr(Line, LINE_LEN_LONG);
  857.             LineCount = 0;
  858.             }
  859.             else {
  860.             sprintf(Line, "%s > ", FileStack[FileStackPtr-1].Name);
  861.             LineCount = strlen(Line);
  862.             if (fgets(TLine, LINE_LEN_LONG,
  863.                     FileStack[FileStackPtr-1].f) == NULL) {
  864.                 /* Its end of file - close it and update stack. */
  865.             TLine[0] = 0;
  866.                 fclose(FileStack[--FileStackPtr].f);
  867.             }
  868.  
  869.             /* Strip off CR/LF/TAB.  */
  870.             for (i = LineCount, p = TLine; *p != 0; p++) {
  871.             if (*p == 0x09)
  872.                 do {
  873.                 Line[i++] = ' ';
  874.                 }
  875.                     while ((i - LineCount) % 8 != 0);
  876.             else if (*p < ' ' || *p > '~')
  877.                 break;
  878.             else
  879.                 Line[i++] = *p;
  880.             }
  881.             Line[i] = 0;
  882.             }
  883.             AliasExpand(&Line[LineCount]);    /* Expand aliases in line. */
  884.  
  885.         if (GetEchoSrc())
  886. #if defined(__MSDOS__) || defined(DJGCC)
  887.             WndwInputWindowPutStr(Line);
  888. #else
  889.             if (FileStackPtr != 0)     /* Input was from keyboard? */
  890.             WndwInputWindowPutStr(Line);
  891. #endif /* __MSDOS__ || DJGCC */
  892.  
  893.             LineLength = strlen(Line);
  894.         } while (LineCount >= LineLength);
  895.  
  896.     c = Line[LineCount++];
  897.     if (c == '#') {              /* Its a comment - skip that line. */
  898.             c = ' ';                   /* Must return something. */
  899.             LineCount = LineLength;    /* Force next time to fetch new line. */
  900.     }
  901. #    ifdef DEBUG
  902.         fprintf(stderr, "%c", c);
  903. #    endif /* DEBUG */
  904.     }
  905.     else {
  906.     c = UnGetChar;
  907.     UnGetChar = 0;
  908.     }
  909.  
  910.     return c;
  911. }
  912.  
  913. /*****************************************************************************
  914. *   Routine to unget one char                             *
  915. *****************************************************************************/
  916. static void InptPrsrUnGetC(char c)
  917. {
  918.     UnGetChar = c;
  919. }
  920.  
  921. /*****************************************************************************
  922. *   Routine to read data up to the next end of expression marker - ';'.         *
  923. *****************************************************************************/
  924. static void FlushToEndOfExpr(int FlushStdin)
  925. {
  926.     if (FileStackPtr > 0)    /* Close all the open files - back to stdin. */
  927.     while (FileStackPtr) fclose(FileStack[--FileStackPtr].f);
  928.     else if (FlushStdin && IPGlblILastToken != TOKENEND)
  929.     while (InptPrsrGetC() != ';');
  930.  
  931. }
  932.  
  933. /*****************************************************************************
  934. *   Routine to push new file to read on the FileStack from INCLUDE command:  *
  935. *****************************************************************************/
  936. void FileInclude(char *PrmFileName)
  937. {
  938.     int i;
  939.     FILE *f;
  940.     char s[LINE_LEN], FileName[LINE_LEN], c;
  941.  
  942.     if (FileStackPtr < FILE_STACK_SIZE) {
  943.     strcpy(FileName, PrmFileName);
  944.     if (strrchr(FileName, '.') == NULL)            /* If no '.' in name */
  945.     {      /* (nor in its path - actually a bug, but I'll skip that...). */
  946.         strcat(FileName, ".irt");
  947.     }
  948.     if ((f = fopen(FileName, "r")) != NULL) {
  949.         FileStack[FileStackPtr].f = f;
  950.         for (i = strlen(FileName)-1;       /* Isolate the file name. */
  951.          i > 0 && (c = FileName[i]) != '\\' && c != '/' && c != ':';
  952.          i--);
  953.         if (i > 0) i++;
  954.         strncpy(FileStack[FileStackPtr].Name, &FileName[i],
  955.                             FILE_NAME_LEN-1);
  956.         FileStackPtr++;         /* Now next char is from that file! */
  957.     }
  958.     else {
  959.         sprintf(s, "Cannt open file %s - ignored", FileName);
  960.         WndwInputWindowPutStr(s);
  961.     }
  962.     }
  963.     else
  964.     WndwInputWindowPutStr("File nesting too deep - ignored");
  965. }
  966.  
  967. /*****************************************************************************
  968. *   Routine to return parsing error if happen one, zero    elsewhere         *
  969. *****************************************************************************/
  970. InptPrsrEvalErrType InptPrsrParseError(char **Message)
  971. {
  972.     InptPrsrEvalErrType Temp;
  973.  
  974.     *Message = IPGlblCharData;
  975.     Temp = IPGlblParseError;
  976.     IPGlblParseError = IPE_NO_ERR;
  977.     return Temp;
  978. }
  979.